<!DOCTYPE html>
<html class="no-js" lang="zh-cn">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title>Python垃圾回收机制 - 唐宋缘明卿的博客</title>
	<script>(function(d,e){d[e]=d[e].replace("no-js","js");})(document.documentElement,"className");</script>
	<script type="text/javascript" src="/js/jquery.min.js"></script>
	
	<meta name="description" content="">
	<meta name="generator" content="Hugo 0.68.3" />
	
	
	<link rel="dns-prefetch" href="//fonts.googleapis.com">
	<link rel="dns-prefetch" href="//fonts.gstatic.com">
	<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700">
	<link rel="stylesheet" href="/ghhs_technology_blog/css/style.css">	
	<link rel="stylesheet" type="text/css" href="/ghhs_technology_blog/css/highlight.css">
	
	
	
	<link rel="shortcut icon" href="/ghhs_technology_blog/favicon.ico">
		
	
</head>
<body class="body">
	<div class="container container--outer">
		<header class="header">
	<div class="container">
		<div class="logo">
			<a class="logo__link" href="/ghhs_technology_blog/" title="唐宋缘明卿的博客" rel="home">
			<div class="logo__title">唐宋缘明卿的博客</div>			
				 
			</a>
			
		</div>
		
<nav class="menu">
	<button class="menu__btn" aria-haspopup="true" aria-expanded="false" tabindex="0">
		<span class="menu__btn-title" tabindex="-1">选择</span>
	</button>
	<ul class="menu__list">
		<li class="menu__item">
			<a class="menu__link" href="/ghhs_technology_blog/about/">关于我</a>
		</li>
		<li class="menu__item">
			<a class="menu__link" href="/ghhs_technology_blog/post/">博客</a>
		</li>
		<li class="menu__item">
			<a class="menu__link" href="/ghhs_technology_blog/timeline/">时间轴</a>
		</li>
		<li class="menu__item">
			<a class="menu__link" href="/ghhs_technology_blog/resume/">简历</a>
		</li>
	</ul>
</nav>

	</div>
</header>
		<div class="wrapper flex">
			<div class="primary">
			
<main class="main" role="main">
	<article class="post">
		<header class="post__header">
			<h1 class="post__title">Python垃圾回收机制</h1>
			<div class="post__meta meta">
<div class="meta__item-datetime meta__item">
	<svg class="meta__icon icon icon-time" width="16" height="14" viewBox="0 0 30 28"><path d="M15 0C7 0 1 6 1 14s6 14 14 14 14-6 14-14S23 0 15 0zm0 25C9 25 4 20 4 14S9 3 15 3s11 5 11 11-5 11-11 11zm1-18h-2v8.4l6.8 4.4L22 18l-6-3.8V7z"/></svg>
	<time class="meta__text" datetime="2016-04-03T14:36:41">April 03, 2016</time>
</div>
<div class="meta__item-datetime meta__item">
	<svg class="meta__icon icon icon-time" width="16" height="14" viewBox="0 0 30 28"><path d="M15 0C7 0 1 6 1 14s6 14 14 14 14-6 14-14S23 0 15 0zm0 25C9 25 4 20 4 14S9 3 15 3s11 5 11 11-5 11-11 11zm1-18h-2v8.4l6.8 4.4L22 18l-6-3.8V7z"/></svg>
	<time class="meta__text">5 分钟</time>
</div></div>
		</header>
<div class="content post__content clearfix">
			<p>这个问题也是目前面试中经常被问到的问题了，因此这里想要详细写一下记录一下。这次从以下三点：引用计数、标记-清除、分代回收来详细的介绍一下。</p>
<h2 id="一引用计数">一、引用计数</h2>
<p>引用计数是<code>GC</code>主要方法，意思就是一个对象在它刚被<code>New</code>出来的时候因为被<code>New</code>方法引用了所以他的引用计数就是1，如果它被引用（也就是在之前的基础上,例如：<code>b=a</code>，被丢入函数列表等等被引用就会在引用计数上加1），如果引用它的对象被删除的时候（在之前的基础上<code>DEL b</code>）那么它的引用计数就会减少一一直到当它的引用计数变为0的时候，垃圾回收机制就会找上门做掉（回收）</p>
<p>那么接下来看一下引用计数方法的优缺点。</p>
<h3 id="优点">优点：</h3>
<ul>
<li>简单，实时性（一旦为零就不跟你多BB，做掉）</li>
<li>运行期没有停顿 可以类比一下Ruby的垃圾回收机制，也就是 <code>实时性</code> 一旦没有引用，内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处：处理回收内存的时间分摊到了平时。</li>
<li>对象有确定的生命周期</li>
<li>易于实现</li>
</ul>
<h3 id="缺点">缺点：</h3>
<ul>
<li>维护引用计数消耗资源，维护引用计数的次数和引用赋值成正比。</li>
<li>无法解决循环引用的问题。A和B相互引用而再没有外部引用A与B中的任何一个，它们的引用计数都为1，但显然应该被回收。
循环引用的示例：</li>
</ul>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-python" data-lang="python">list1 <span style="color:#f92672">=</span> []
list2 <span style="color:#f92672">=</span> []
list1<span style="color:#f92672">.</span>append(list2)
list2<span style="color:#f92672">.</span>append(list1)
</code></pre></div><p>引用计数无法解决此类问题，因此需要引入新的机制。</p>
<hr>
<h2 id="标记-清除">标记-清除</h2>
<p>标记清除就是用来解决循环引用的问题。<br>
只有容器对象才会出现引用循环，比如列表、字典、类、元组。 首先，为了追踪容器对象，需要每个容器对象维护两个额外的指针， 用来将容器对象组成一个链表，指针分别指向前后两个容器对象，方便插入和删除操作。<br>
在标记-清除算法中，有两个集中营，一个是<code>root</code>链表<code>(root object)</code>，另外一个是<code>unreachable</code>链表。</p>
<h5 id="qa为什么要搞这两个链表">QA： 为什么要搞这两个链表</h5>
<p>之所以要剖成两个链表，是基于这样的一种考虑：现在的<code>unreachable</code>可能存在被<code>root</code>链表中的对象，直接或间接引用的对象，这些对象是不能被回收的，一旦在标记的过程中，发现这样的对象，就将其从<code>unreachable</code>链表中移到<code>root</code>链表中；当完成标记后，<code>unreachable</code>链表中剩下的所有对象就是名副其实的垃圾对象了，接下来的垃圾回收只需限制在<code>unreachable</code>链表中即可。</p>
<p>简单来说就是对已标记的对象进行重新筛查，随后真正删除。</p>
<blockquote>
<p>标记清除（Mark—Sweep）』算法是一种基于追踪回收（<code>tracing GC</code>）技术实现的垃圾回收算法。它分为两个阶段：第一阶段是标记阶段，GC会把所有的『活动对象』打上标记，第二阶段是把那些没有标记的对象『非活动对象』进行回收。<br>
那么GC又是如何判断哪些是活动对象哪些是非活动对象的呢？<br>
对象之间通过引用（指针）连在一起，构成一个有向图，对象构成这个有向图的节点，而引用关系构成这个有向图的边。从根对象（root object）出发，沿着有向边遍历对象，可达的（reachable）对象标记为活动对象，不可达的对象就是要被清除的非活动对象。根对象就是全局变量、调用栈、寄存器。</p>
</blockquote>
<p><img src="https://gitee.com/gaohaihang/ghhs_technology_blog/raw/master/img/res/%E6%A0%87%E8%AE%B0-%E6%B8%85%E9%99%A4.png" alt=""></p>
<blockquote>
<p>在上图中，我们把小黑圈视为全局变量，也就是把它作为root object，从小黑圈出发，对象1可直达，那么它将被标记，对象2、3可间接到达也会被标记，而4和5不可达，那么1、2、3就是活动对象，4和5是非活动对象会被GC回收。</p>
</blockquote>
<hr>
<h2 id="分代回收">分代回收</h2>
<p>说到分代回收，就不得不提到gc的阈值和逻辑。<br>
<code>GC</code>的阈值，所谓阈值就是一个临界点的值。<br>
随着你的程序运行，Python解释器保持对新创建的对象，以及因为引用计数为零而被释放掉的对象的追踪。<br>
下面是<code>GC</code>的逻辑:</p>
<pre><code>1. -&gt; 1、分配内存
2. -&gt; 发现超过阈值了
3. -&gt; 触发垃圾回收
4. -&gt; 将所有可收集对象链表放到一起
5. -&gt; 遍历, 计算有效引用计数
6. -&gt; 分成 有效引用计数=0 和 有效引用计数 &gt; 0 两个集合
7. -&gt; 大于0的, 放入到更老一代
8. -&gt; =0的, 执行回收
9. -&gt; 回收遍历容器内的各个元素, 减掉对应元素引用计数(破掉循环引用)
10. -&gt; 执行-1的逻辑, 若发现对象引用计数=0, 触发内存回收
11. -&gt; python底层内存管理机制回收内存
</code></pre><pre><code>                    垃圾回收=垃圾检测+释放。
</code></pre>
<p>分代回收思想将对象分为三代（generation 0,1,2），0代表幼年对象，1代表青年对象，2代表老年对象。<br>
根据弱代假说（越年轻的对象越容易死掉，老的对象通常会存活更久。） <br>
新生的对象被放入0代，如果该对象在第0代的一次gc垃圾回收中活了下来，那么它就被放到第1代里面（它就升级了）。<br>
如果第1代里面的对象在第1代的一次gc垃圾回收中活了下来，它就被放到第2代里面。</p>
<blockquote>
<p>可以通过gc.set_threshold(threshold0[,threshold1[,threshold2]])设置gc每一代垃圾回收所触发的阈值。</p>
</blockquote>
<p>从上一次第0代gc后，如果分配对象的个数减去释放对象的个数大于threshold0，那么就会对第0代中的对象进行gc垃圾回收检查。   
从上一次第1代gc后，如过第0代被gc垃圾回收的次数大于threshold1，那么就会对第1代中的对象进行gc垃圾回收检查。同样，从上一次第2代gc后，如过第1代被gc垃圾回收的次数大于threshold2，那么就会对第2代中的对象进行gc垃圾回收检查。</p>
<h3 id="分代回收总结">分代回收总结:</h3>
<p>  分代回收是一种以空间换时间的操作方式，Python将内存根据对象的存活时间划分为不同的集合，每个集合称为一个代，Python将内存分为了三代，分别为年轻代（第0代）、中年代（第1代）、老年代（第2代），他们对应的是3个链表，它们的垃圾收集频率与对象的存活时间的增大而减小。</p>

		</div>
		
	</article>
</main>







			</div>
			<aside class="sidebar"><div class="widget-search widget">
	<form class="widget-search__form" role="search" method="get" action="https://google.com/search">
		<label>
			<input class="widget-search__field" type="search" placeholder="搜索..." value="" name="q" aria-label="搜索...">
		</label>
		<input class="widget-search__submit" type="submit" value="Search">
		<input type="hidden" name="sitesearch" value="/ghhs_technology_blog/" />
	</form>
</div>
<div class="widget-recent widget">
	<h4 class="widget__title">近期文章</h4>
	<div class="widget__content">
		<ul class="widget__list">
			<li class="widget__item"><a class="widget__link" href="/ghhs_technology_blog/post/jwt%E5%AE%8C%E5%85%A8%E6%94%BB%E7%95%A5%E7%89%88/">JWT完全攻略</a></li>
			<li class="widget__item"><a class="widget__link" href="/ghhs_technology_blog/post/%E9%82%A3%E4%BA%9B%E5%B9%B4%E8%B8%A9%E8%BF%87%E7%9A%84%E5%9D%91/">踩过的坑，流过的泪</a></li>
			<li class="widget__item"><a class="widget__link" href="/ghhs_technology_blog/post/github%E6%90%9C%E7%B4%A2%E6%8A%80%E5%B7%A7/">Github搜索技巧汇总</a></li>
			<li class="widget__item"><a class="widget__link" href="/ghhs_technology_blog/post/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/">单例模式&amp;实现方法&amp;避雷</a></li>
			<li class="widget__item"><a class="widget__link" href="/ghhs_technology_blog/post/%E5%88%9D%E8%AF%86celery%E5%BC%82%E6%AD%A5%E9%82%AE%E7%AE%B1%E9%AA%8C%E8%AF%81/">初识Celery&amp;异步邮箱验证</a></li>
			<li class="widget__item"><a class="widget__link" href="/ghhs_technology_blog/post/%E5%88%9D%E8%AF%86scrapy%E6%A1%86%E6%9E%B6/">初识Scrapy框架</a></li>
			<li class="widget__item"><a class="widget__link" href="/ghhs_technology_blog/post/git%E4%B9%8B%E4%B8%80%E9%94%AE%E4%B8%89%E8%BF%9E%E5%90%88%E4%BD%9C%E5%BC%80%E5%8F%91/">Git之一键三连&amp;合作开发</a></li>
			<li class="widget__item"><a class="widget__link" href="/ghhs_technology_blog/post/python%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%9C%BA%E5%88%B6/">Python垃圾回收机制</a></li>
			<li class="widget__item"><a class="widget__link" href="/ghhs_technology_blog/post/python23%E7%9A%84%E5%8C%BA%E5%88%AB/">python2&amp;3的区别</a></li>
			<li class="widget__item"><a class="widget__link" href="/ghhs_technology_blog/post/one/">HELLO WORLD!</a></li>
		</ul>
	</div>
</div>
<div class="widget-social widget">
	<h4 class="widget-social__title widget__title">社群</h4>
	<div class="widget-social__content widget__content">
		<div class="widget-social__item widget__item">
			<a class="widget-social__link widget__link btn" title="Facebook" rel="noopener noreferrer" href="https://facebook.com/username" target="_blank">
				<svg class="widget-social__link-icon icon-facebook" viewBox="0 0 352 352" width="24" height="24" fill="#fff"><path d="m0 32v288c0 17.5 14.5 32 32 32h288c17.5 0 32-14.5 32-32v-288c0-17.5-14.5-32-32-32h-288c-17.5 0-32 14.5-32 32zm320 0v288h-83v-108h41.5l6-48h-47.5v-31c0-14 3.5-23.5 23.5-23.5h26v-43.5c-4.4-.6-19.8-1.5-37.5-1.5-36.9 0-62 22.2-62 63.5v36h-42v48h42v108h-155v-288z"/></svg>
				<span>Facebook</span>
			</a>
		</div>
		<div class="widget-social__item widget__item">
			<a class="widget-social__link widget__link btn" title="Instagram" rel="noopener noreferrer" href="https://www.instagram.com/username" target="_blank">
				<svg class="widget-social__link-icon icon-instagram" viewBox="0 0 256 256" width="24" height="24" fill="#fff"><circle cx="193" cy="59" r="15"/><path fill-rule="evenodd" d="M101 0h54c41 0 58.4 3.9 74.5 17C256.2 37.5 256 74.8 256 97.7v60c0 26.7 0 60.4-26.5 81.4-16 13.4-33.5 16.9-74.5 16.9h-54c-41 0-57.5-3.5-74.5-16.9C1 218.9.5 186.3.1 160.5L0 155V97.7c0-23-.2-60.2 26.5-80.7C45 2 60 0 101 0zm4.9 23h44.3c45.8 0 58.3 3.5 70.3 17.5 11.8 13.2 12 30.1 12.5 62.9V156c.2 20.8.3 45.8-12.5 59.5-12 14-24.5 17.5-70.3 17.5h-44.3c-45.9 0-57.3-3.5-70.4-17.5-12.2-13-12.3-36.5-12.4-56.7v-55.6c.4-32.6.7-49.6 12.4-62.7C48 26.5 60 23 105.9 23zm19.6 144.5a42 42 0 1 0 0-84 42 42 0 0 0 0 84zm0 22.5a64.5 64.5 0 1 0 0-129 64.5 64.5 0 0 0 0 129z"/></svg>
				<span>Instagram</span>
			</a>
		</div>
		<div class="widget-social__item widget__item">
			<a class="widget-social__link widget__link btn" title="Telegram" rel="noopener noreferrer" href="https://t.me/2333333" target="_blank">
				<svg class="widget-social__link-icon icon-telegram" viewBox="0 0 132 110" width="24" height="24"><path fill="#ddd" d="M50 103c-4 0-3-1-5-5L34 60l88-52"/><path fill="#aaa" d="M50 103c3 0 4-1 6-3l16-16-20-12"/><path fill="#fff" d="M52 72l48 36c6 3 10 2 11-5l20-93c2-8-3-11-8-9L7 45c-8 4-8 8-1 10l29 9 69-43c3-2 6-1 4 1"/></svg>
				<span>Telegram</span>
			</a>
		</div>
		<div class="widget-social__item widget__item">
			<a class="widget-social__link widget__link btn" title="GitHub" rel="noopener noreferrer" href="https://github.com/username" target="_blank">
				<svg class="widget-social__link-icon icon-github" viewBox="0 0 384 374" width="24" height="24" fill="#fff"><path d="m192 0c-106.1 0-192 85.8-192 191.7 0 84.7 55 156.6 131.3 181.9 9.6 1.8 13.1-4.2 13.1-9.2 0-4.6-.2-16.6-.3-32.6-53.4 11.6-64.7-25.7-64.7-25.7-8.7-22.1-21.3-28-21.3-28-17.4-11.9 1.3-11.6 1.3-11.6 19.3 1.4 29.4 19.8 29.4 19.8 17.1 29.3 44.9 20.8 55.9 15.9 1.7-12.4 6.7-20.8 12.2-25.6-42.6-4.8-87.5-21.3-87.5-94.8 0-20.9 7.5-38 19.8-51.4-2-4.9-8.6-24.3 1.9-50.7 0 0 16.1-5.2 52.8 19.7 15.3-4.2 31.7-6.4 48.1-6.5 16.3.1 32.7 2.2 48.1 6.5 36.7-24.8 52.8-19.7 52.8-19.7 10.5 26.4 3.9 45.9 1.9 50.7 12.3 13.4 19.7 30.5 19.7 51.4 0 73.7-44.9 89.9-87.7 94.6 6.9 5.9 13 17.6 13 35.5 0 25.6-.2 46.3-.2 52.6 0 5.1 3.5 11.1 13.2 9.2 76.2-25.5 131.2-97.3 131.2-182 0-105.9-86-191.7-192-191.7z"/></svg>
				<span>GitHub</span>
			</a>
		</div>
		<div class="widget-social__item widget__item">
			<a class="widget-social__link widget__link btn" title="Email" href="mailto:g617415200@gmail.com">
				<svg class="widget-social__link-icon icon-mail" viewBox="0 0 416 288" width="24" height="24" fill="#fff"><path d="m0 16v256 16h16 384 16v-16-256-16h-16-384-16zm347 16-139 92.5-139-92.5zm-148 125.5 9 5.5 9-5.5 167-111.5v210h-352v-210z"/></svg>
				<span>g617415200@gmail.com</span>
			</a>
		</div>
	</div>
</div>

</aside>
		</div>
		<footer class="footer">
	<div class="container footer__container flex">
		
		<div class="footer__copyright">
			&copy; 2020 唐宋缘明卿的博客.
			<span class="footer__copyright-credits">使用 <a href="https://gohugo.io/" rel="nofollow noopener" target="_blank">Hugo</a> 技术与 <a href="https://github.com/kingfsen/Mainroad/" rel="nofollow noopener" target="_blank">Mainroad</a>主题</span>
		</div>
	</div>
</footer>
	</div>
<script async defer src="/ghhs_technology_blog/js/menu.js"></script>
<script async src="/ghhs_technology_blog/js/highlight.js"></script>

<script type="text/javascript">
    (function(){
        $("pre code").parent().addClass("line-numbers")
    }())
</script>

</body>
</html>